import { Options, DEFAULT, Scroll, Column } from "./type"
import { debounce, getValueByModel, isChinese, isEnglish } from "./util";

export default class FarmentCanvas{
    root?: HTMLElement;
    options?: Options;
    canvas?: HTMLCanvasElement;
    columnsWidths: number[] = []

    checkCellFunctions: Function[] = [];

    constructor(root: HTMLElement, options: Options){
        this.root = root;
        this.options = options;
        this.canvas = this.createCanvas();
        this.root.appendChild(this.canvas);
        this.addEvent();
    }

    createCanvas(){
        const canvas = document.createElement('canvas');
        canvas.width = this.root?.clientWidth ?? DEFAULT.CANVAS_WIDTH;
        canvas.height = this.root?.clientHeight ?? DEFAULT.CANVAS_HEIGHT;
        canvas.style.backgroundColor = DEFAULT.CANVAS_BACKGOUND_COLOR;
        return canvas;
    }

    getCanvas2d(){
        return this.canvas?.getContext('2d')
    }

    /**
     * 获取某列的宽度
     * @param column 
     * @returns number
     */
    getColumnWidth(column: Column): number{
        if(column.width) return column.width;

        const { columns } = this.options!
        // const { width } = this.canvas!
        // return Math.floor(width / columns.length) // 测试限宽

        return this.columnsWidths[columns.findIndex(col => col === column)]
    }

    getColumnWidths(){
        const { columns, dataSource } = this.options!
        const { width } = this.canvas!
        const maxWidths: number[] = []
        const gtEnvWidthsTotal: number[] = []

        columns.forEach((col, index) => {
            const maxWidthInColumn = Math.max(
                this.computedTextWidth(col.title) + DEFAULT.CELL_PADDING_LEFT + DEFAULT.CELL_PADDING_RIGHT,
                ...dataSource.map(row => {
                    const w = this.computedTextWidth(getValueByModel(col.dataindex, row)) + DEFAULT.CELL_PADDING_LEFT + DEFAULT.CELL_PADDING_RIGHT;
                    return w
                })
            )
            if (maxWidthInColumn <= width / columns.length) {
                maxWidths[index] = maxWidthInColumn;
            } else {
                gtEnvWidthsTotal[index] = maxWidthInColumn;
            }
        }) 

        // 均衡超过均值的列宽
        gtEnvWidthsTotal.forEach((item, index) => {
            const total = gtEnvWidthsTotal.reduce((p, v) => (p || 0) + (v || 0))
            const env = total / gtEnvWidthsTotal.filter(v => v).length;

            if(item > env) gtEnvWidthsTotal[index] = env
        })

        // 剩余分配的总列宽
        const totalGtEnv = width - maxWidths.reduce((p, v) => (p || 0) + (v || 0))
        columns.forEach((_, index) => {
            if (!maxWidths[index]) {
                maxWidths[index] = totalGtEnv * gtEnvWidthsTotal[index] / gtEnvWidthsTotal.reduce((p, v) => (p || 0) + (v || 0))
            }
        })

        return maxWidths.map((v: number, index: number) => {
            if (index === maxWidths.length - 1) return Math.ceil(width - maxWidths.slice(0, -1).reduce((p, c) => p + c)) - 2 // 保证最后一根线渲染正常
            return Math.round(v)
        })
    }

    /**
     * 计算文本宽度
     * @param text 
     * @returns 
     */
    computedTextWidth(text: string){
        const ctx = this.getCanvas2d()!;
        const { fontSize } = this.options!
        ctx.font = `${fontSize}px Arial`;
        const { width } = ctx.measureText(text);
        return width;
    }

    /**
     * 绘制单元格边框
     * @param x 
     * @param y 
     * @param w 
     * @param h 
     */
    drawCellRect(x: number, y: number, w: number, h: number, option?: {
        backgroundColor?: string
        textColor?: string
    }){
        const ctx = this.getCanvas2d()!;

        // 设置背景色
        if (option?.backgroundColor) {
            ctx.fillStyle = option.backgroundColor;
            ctx.fillRect(x+ 0.5, y+ 0.5, w, h);
        }
        
        ctx.strokeStyle = '#666'; // 设置边框颜色为黑色
        ctx.lineWidth = 0.5;
        ctx.strokeRect(x+ 0.5, y+ 0.5, w, h);
    }
    /**
     * 绘制单元格内容
     * @param x x轴坐标
     * @param y y轴坐标
     * @param text 绘制文本
     * @param maxWidth 最大绘制宽度
     */
    drawCellText(x: number, y: number, text: string, maxWidth?: number, option?: {
        font?: string
    }) {
        const ctx = this.getCanvas2d()!;
        const fontSize = this.options?.fontSize!;
        const warpText = this.getTextWarpArray(text, maxWidth);
        const lineHeight = this.getLineHeight()
        ctx.fillStyle = '#000';
        ctx.font = option?.font || `${fontSize}px Arial`;
        warpText.forEach(t => {
            ctx.fillText(t, x + DEFAULT.CELL_PADDING_LEFT, y + DEFAULT.CELL_PADDING_TOP + fontSize);
            y += lineHeight
        })
    }

    /**
     * 绘制表头
     * @returns 表头高度
     */
    drawThead(x: number, y: number): number{
        const { columns, fontSize } = this.options!
        // 行高度
        const h = this.getTheadHeight()

        columns.forEach(column => {
            const w = this.getColumnWidth(column)
            this.drawCellRect(x, y, w, h, { backgroundColor: "#ccc" })
            // 事件定位检测函数
            this.checkCellFunctions.push(function (x:number, y: number, offsetX: number, offsetY: number) {
                if (
                    offsetX > x && 
                    offsetX < x + w && 
                    offsetY > y && 
                    offsetY < y + h
                ) {
                    return {
                        column,
                        text: undefined,
                        record: undefined,
                        rowindex: undefined,
                        width: w,
                        height: h,
                        left: x,
                        top: y,
                    }
                }
            }.bind(this, x, y))

            const text = column.title
            // 格子高度
            const cellHeight = this.getTextWarpArray(text, w).length * this.getLineHeight()  + DEFAULT.CELL_PADDING_TOP + DEFAULT.CELL_PADDING_BOTTOM

            if (cellHeight === h){
                this.drawCellText( x, y, text, w, { font: `bold ${fontSize}px Arial` }); 
            } else {
                this.drawCellText( x, y + h / 2 - cellHeight / 2, text, w, { font: `bold ${fontSize}px Arial` });
            }
            x += w
        })

        return h
    }
    /**
     * 绘制数据行
     * @param data 行数据
     * @returns 返回行高度
     */
    drawRow(x: number, y: number, data: Object): number{
        const { columns, fontSize } = this.options!
        // 行高度
        let h = this.getRowHeight(data);

        columns.forEach(column => {
            const w = this.getColumnWidth(column)
            this.drawCellRect(x, y, w, h)

            const text = getValueByModel(column.dataindex, data)
            // 事件定位检测函数
            this.checkCellFunctions.push(function (x:number, y: number, offsetX: number, offsetY: number) {
                if (
                    offsetX > x && 
                    offsetX < x + w && 
                    offsetY > y && 
                    offsetY < y + h
                ) {
                    return {
                        column,
                        text: text,
                        record: data,
                        rowindex: undefined,
                        width: w,
                        height: h,
                        left: x,
                        top: y,
                    }
                }
            }.bind(this, x, y))
            // 格子高度
            const cellHeight = this.getTextWarpArray(text, w).length * this.getLineHeight()  + DEFAULT.CELL_PADDING_TOP + DEFAULT.CELL_PADDING_BOTTOM

            if (cellHeight === h){
                this.drawCellText( x, y, text, w); 
            } else {
                this.drawCellText( x, y + h / 2 - cellHeight / 2, text, w);
            }
            x += w
        })

        return h
    }
    /**
     * 获取文本行高
     * @returns 
     */
    getLineHeight(): number{
        const fontSize = this.options?.fontSize!;
        return fontSize * 1.5; // 字体行高
    }
    /**
     * 获取表头最大高度
     * @returns 
     */
    getTheadHeight(): number{
        const { columns } = this.options!
        const lineHeight = this.getLineHeight()
        const heights: number[] = columns.map((column: Column) => {
            const maxTextWidth = this.getColumnWidth(column) - DEFAULT.CELL_PADDING_LEFT - DEFAULT.CELL_PADDING_RIGHT;
            return this.getTextWarpArray(column.title, maxTextWidth).length * lineHeight
        })
        return Math.max(...heights) + DEFAULT.CELL_PADDING_TOP + DEFAULT.CELL_PADDING_BOTTOM;
    }
    /**
     * 获取数据行的最大高度
     * @param data 
     * @returns 
     */
    getRowHeight(data: Object): number{
        const { columns } = this.options!
        const lineHeight = this.getLineHeight()
        const heights: number[] = columns.map((column: Column) => {
            return this.getTextWarpArray(
                getValueByModel(column.dataindex, data), 
                this.getColumnWidth(column)
            ).length * lineHeight + DEFAULT.CELL_PADDING_TOP + DEFAULT.CELL_PADDING_BOTTOM
        })
        return Math.max(...heights);
    }
    /**
     * 将文本在宽度范围内计算换行
     * @param text 
     * @param maxWidth 
     * @returns 
     */
    getTextWarpArray(text: string, maxWidth?: number): string[]{
        if (!maxWidth) return [text];
        const ctx = this.getCanvas2d()!;
        const { width } = ctx.measureText(text);

        // 将字符串按分隔符切成字符串数组
        const tranWarps = (splitCode: string): string[] => {
            const warpText = []
            let catchText = ''
            const arr = text.split(splitCode)

            for(const word of arr) {
                // 已超出
                const currentWidth = this.computedTextWidth(catchText + word)
                if (currentWidth > maxWidth) {
                    warpText.push(catchText);
                    catchText = word;
                } else {
                    catchText += word
                }
            }

            warpText.push(catchText);

            return warpText;
        }
        
        if (width > maxWidth) {
            // 中文
            if (isChinese(text)){
                return tranWarps("")
            }
            // 英文
            if (isEnglish(text)){
                return tranWarps(text.includes(" ") ? " " : '')
            }
            // 混合
            return tranWarps('')
        } else {
            return [ text ];
        }
    }

    /**
     * 绘制表格数据
     */
    draw(dataSource: any[], option?: { drawHead: boolean } ){
        console.time()

        // @ts-ignore
        this.options.dataSource = dataSource
       
        if (!this.columnsWidths.length){
            this.columnsWidths = this.getColumnWidths()
        }
        
        let maxHeight = 0;
        dataSource.forEach(row => maxHeight += this.getRowHeight(row))
        // @ts-ignore
        this.canvas.height = maxHeight + 1;
        
        // 设置背景色
        const ctx = this.getCanvas2d()!;
        ctx.fillStyle = '#f8f8f8'
        // @ts-ignore
        ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);

        let y = 0; 
        // 画表头
        if (option?.drawHead) {
            // @ts-ignore
            this.canvas.height = maxHeight + this.getTheadHeight()
            y += this.drawThead(0, 0);
        }

        dataSource.forEach(row => {
            y += this.drawRow(0, y, row)
        })

        console.timeEnd()
    }

    /**
     * 事件处理
     */
    addEvent(){
        if (this.options?.event?.onclick){
            this.canvas?.addEventListener('click', (e) => {
                for (const fn of this.checkCellFunctions){
                    const result = fn?.(e?.clientX, e?.clientY)
                    if (result) {
                        this.options?.event?.onclick?.(result)
                        return;
                    }
                }
            })
        }
    }
}